package pl.edu.agh.mobicentsVoIP.servlets;

import java.io.IOException;
import java.util.Date;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.sip.Address;
import javax.servlet.sip.Proxy;
import javax.servlet.sip.SipErrorEvent;
import javax.servlet.sip.SipErrorListener;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServlet;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipSession;

import pl.edu.agh.mobicentsVoIP.core.Call;
import pl.edu.agh.mobicentsVoIP.core.CallStatus;
import pl.edu.agh.mobicentsVoIP.core.User;
import pl.edu.agh.mobicentsVoIP.database.MockFacade;
import pl.edu.agh.mobicentsVoIP.utils.Logger;



public class ProxySipServlet extends SipServlet implements SipErrorListener, Servlet{

	private static final long serialVersionUID = 1L;
    private static Logger logger = Logger.getLogger(ProxySipServlet.class);
    private static final String CONTACT_HEADER = "Contact";
    private SipFactory sipFactory;
	private MockFacade facade;
    
    public ProxySipServlet(){
    	facade = MockFacade.getInstance();
    }
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
            super.init(servletConfig);
            logger.info("########################################## Mobicens VoIP System UP!! ##########################################");
            try {                   
                    // Getting the Sip factory from the JNDI Context
                    Properties jndiProps = new Properties();                        
                    Context initCtx = new InitialContext(jndiProps);
                    Context envCtx = (Context) initCtx.lookup("java:comp/env");
                    sipFactory = (SipFactory) envCtx.lookup("sip/pl.edu.agh.mobicentsVoIP.servlets.Application/SipFactory");
                    logger.info("Sip Factory ref from JNDI : " + sipFactory);
            } catch (NamingException e) {
                    throw new ServletException("Uh oh -- JNDI problem !", e);                       
            }
    }
    
    @Override
    protected void doProvisionalResponse(SipServletResponse resp)
    		throws ServletException, IOException {
    	logger.info("doProvisionalResponse");
    	//    	resp.createAck().send();
    }
    
    @Override
    protected void doInvite(SipServletRequest req) throws ServletException,IOException {
            logger.info("Got Invite\n");
                        
            Proxy proxy = req.getProxy();
            proxy.setRecordRoute(true);
            proxy.setSupervised(true);
            proxy.proxyTo(req.getRequestURI());
            
            String fromUri = req.getFrom().getURI().toString();
            String toUri = req.getTo().getURI().toString();
                       
            User from = facade.getUserByUserURI(fromUri);
            User to = facade.getUserByUserURI(toUri);
            
            
            //TODO: remove adding this users
            if(from == null){
            	logger.info("from User is null: " + fromUri);
            	from = new User(fromUri);
            }
            if(to == null){
            	logger.info("to User is null: " + toUri);
            	to = new User(toUri);
            }
            
            Call call = new Call();
            call.setFrom(from);
            call.setTo(to);
            call.setProxy(proxy);
            call.setStatus(CallStatus.WAITING);
            facade.addCall(call);
            
            req.getSession().setAttribute("calling", "WTF");
            req.getSession().setAttribute("call", call);
            call.addSession(req.getSession());
            
            logger.info("successfuly invite from: " + from.toString() + " to: " + to.toString() );
            facade.logState(logger);
    }  
    
    
    @Override
    protected void doAck(SipServletRequest req) throws ServletException,
    		IOException {
    	logger.info("Got Ack!");
    	
    	SipSession session = req.getSession();
    	
    	
    	if(session.getAttribute("calling") != null){
        	Call call = (Call) session.getAttribute("call");
        	call.setStatus(CallStatus.CONNECTED);
        	call.setStartDate(new Date());
        	call.getProxy().setSupervised(false);
    	}
    	    	
    	session.removeAttribute("call");
    	session.removeAttribute("calling");
    	facade.logState(logger);
    }
    
    @Override
    protected void doCancel(SipServletRequest req) throws ServletException,
    		IOException {
    	logger.info("Get cancel!");
    	SipSession session = req.getSession();
    	    	
    	if(session.getAttribute("calling") != null){
        	Call call = (Call) session.getAttribute("call");
        	Date date = new Date();
        	call.setStartDate(date);
        	call.setEndDate(date);
        	call.end();
    	}
    	
    	session.removeAttribute("call");
    	session.removeAttribute("calling");
    	facade.logState(logger);
    }
    

     
    @Override
    protected void doOptions(SipServletRequest req) throws ServletException,IOException {
            logger.info("doOptions");
            req.createResponse(SipServletResponse.SC_OK).send();
    }	
    protected void doRegister(SipServletRequest req) throws ServletException, IOException {
        logger.info("doRegister");
        int response = SipServletResponse.SC_OK;
        SipServletResponse resp = req.createResponse(response);
        
        Address address = req.getAddressHeader(CONTACT_HEADER);
        User user = facade.getUserByUserURI(req.getFrom().getURI().toString());
        
        if(user == null){
        	user = new User(req.getFrom().getURI().toString());
        }
        
        int expires = address.getExpires();
        if(expires < 0) {
                expires = req.getExpires();
        }
        if(expires == 0) {
                facade.unRegisterUser(user);
                logger.info("User " + user + " unregistered");
        } else {
                resp.setAddressHeader(CONTACT_HEADER, address);
                facade.registerUser(user);
                logger.info("User " + user + " registered with an Expire time of " + expires);
        }                               
                    
        facade.logState(logger);
        resp.send();
    }
        
    @Override
    protected void doErrorResponse(SipServletResponse resp) throws ServletException, IOException {
    	logger.info("Error Response");
    	SipSession session = resp.getSession();
    	
    	if(session.getAttribute("calling") != null){
        	Call call = (Call) session.getAttribute("call");
        	Date date = new Date();
        	call.setStartDate(date);
        	call.setEndDate(date);
        	call.end();
        	facade.endCall(call); 
        	call.setStatus(CallStatus.TERMINATED);
        	call.getProxy().setSupervised(false);
    	}
    	facade.logState(logger);
    }
    @Override
    protected void doBye(SipServletRequest request) throws ServletException, IOException {
            logger.info("doBye");
            SipSession session = request.getSession();
            SipSession linkedSession = (SipSession) session.getAttribute("LinkedSession");
            if (linkedSession != null) {
                    SipServletRequest bye = linkedSession.createRequest("BYE");
                    logger.info("Sending bye to " + linkedSession.getRemoteParty());
                    bye.send();
            }
            Call call = facade.getCallByUsersURIs(request.getFrom().getURI().toString(), request.getTo().getURI().toString());
            
            if(call == null){
            	call = facade.getCallByUsersURIs(request.getTo().getURI().toString(), request.getFrom().getURI().toString());
            }
            if(call != null){
	            call.end();
	        	facade.endCall(call);
	        	call.setEndDate(new Date());
            }
            SipServletResponse ok = request.createResponse(SipServletResponse.SC_OK);
            facade.logState(logger);
            ok.send();
    }
        
    @Override
    protected void doSuccessResponse(SipServletResponse resp) throws ServletException, IOException {
    	logger.info("SuccessResponse");
//        SipSession session = resp.getSession();
//        if (resp.getStatus() == SipServletResponse.SC_OK) {
//        	Boolean inviteSent = (Boolean) session.getAttribute("InviteSent");
//        	if (inviteSent != null && inviteSent.booleanValue()) {
//        		return;
//        	}
//        	Address secondPartyAddress = (Address) resp.getSession()
//        			.getAttribute("SecondPartyAddress");
//        	if (secondPartyAddress != null) {
////        		SipServletRequest invite = sipFactory.createRequest(resp
////        				.getApplicationSession(), "INVITE", session
////        				.getRemoteParty(), secondPartyAddress);
////        		logger.info("Found second party -- sending INVITE to "+ secondPartyAddress);
////        		String contentType = resp.getContentType();
////        		if (contentType.trim().equals("application/sdp")) {
////        			invite.setContent(resp.getContent(), "application/sdp");
////        		}
////        		session.setAttribute("LinkedSession", invite.getSession());
////                invite.getSession().setAttribute("LinkedSession", session);
////
////                SipServletRequest ack = resp.createAck();
////                invite.getSession().setAttribute("FirstPartyAck", ack);
////                invite.getSession().setAttribute("FirstPartyContent", resp.getContent());
////                            
////                Call call = (Call) session.getAttribute("call");
////                            
////                // The call links the two sessions, add the new session to the call
////                call.addSession(invite.getSession());
////                invite.getSession().setAttribute("call", call);
////                            
////                invite.send();
////
////                session.setAttribute("InviteSent", Boolean.TRUE);
//            } else {
//            	logger.info("inside else");
//            	String cSeqValue = resp.getHeader("CSeq");
//            	if(cSeqValue.indexOf("INVITE") != -1) {
//            		logger.info("Got OK from second party -- sending ACK");    
//            		SipServletRequest secondPartyAck = resp.createAck();
//            		SipServletRequest firstPartyAck = (SipServletRequest) resp.getSession().getAttribute("FirstPartyAck");    
//                    firstPartyAck.setContent(resp.getContent(), "application/sdp");
//                    secondPartyAck.setContent(resp.getSession().getAttribute("FirstPartyContent"), "application/sdp");
//                    firstPartyAck.send();
//                    secondPartyAck.send();
//                 }
//            }
//        }else if (resp.getStatus() == SipServletResponse.SC_DECLINE){
//        	logger.info("###################################################################################");
//        	logger.info("###################################################################################");
//        	logger.info("###################################################################################");
//        	logger.info("###################################################################################");
//        	logger.info("###################################################################################");
//        	logger.info("###################################################################################");
//        }else{
//        	logger.info(resp.getStatus() + "");
//        }
        facade.logState(logger);
    }
    
    public void noAckReceived(SipErrorEvent ee) {
        logger.info("noAckReceived");
    }    
    public void noPrackReceived(SipErrorEvent ee) {
        logger.info("noPrackReceived");
    }   
}
